package com.ccys.gateway.config.filter;

import com.alibaba.fastjson.JSONObject;
import com.ccys.common.constants.ContextConstants;
import com.ccys.common.constants.TouristUtil;
import com.ccys.common.enums.CodeEnum;
import com.ccys.common.util.JedisUtil;
import com.ccys.common.util.Kit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * 自定义gateway全局过滤器
 */
@Component("customGlobalFilter")
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Value("${spring.profiles.active}")
    private String active;

    //redis路径
    @Value("${spring.redis.host}")
    private String REDIS_HOST;

    //redis端口
    @Value("${spring.redis.port}")
    private Integer REDIS_PORT;

    //redis密码
    @Value("${spring.redis.password}")
    private String REDIS_PASSWORD;

    //超时时间
    private final Long SUB_TIMESTAMP = 60000L * 5L;

    //校验失败json
    private static JSONObject ILLEGAL_JSON = new JSONObject();
    //未登录json
    private static JSONObject NO_LOGIN_JSON = new JSONObject();

    static {
        log.info("===========>开始初始化非法json<==========");
        ILLEGAL_JSON.put("msg", "鉴权失败");
        ILLEGAL_JSON.put("data", "");
    }

    static {
        log.info("===========>开始初始化未登录json<==========");
        NO_LOGIN_JSON.put("msg", "未登录");
        NO_LOGIN_JSON.put("data", "");
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("当前请求id:{}", exchange.getLogPrefix());
        if (StringUtils.isBlank(ContextConstants.REDIS_HOST)) {
            ContextConstants.REDIS_HOST = REDIS_HOST;
            ContextConstants.REDIS_PORT = REDIS_PORT;
            ContextConstants.REDIS_PASSWORD = REDIS_PASSWORD;
        }

        //options请求直接返回成功
        if (RequestMethod.OPTIONS.toString().equals(exchange.getRequest().getMethodValue())) {
            exchange.getResponse().setStatusCode(HttpStatus.OK);
            return exchange.getResponse().setComplete();
        }

        String uri = exchange.getRequest().getURI().toString();

        String method = exchange.getRequest().getMethod().toString();

        if (TouristUtil.isTouristResouce(uri, method)) {
            //鉴权成功标识
            exchange.getRequest().mutate().header("gatewayPass", "ok");
            return chain.filter(exchange);
        }

        //非法请求校验
        if (!"dev".equalsIgnoreCase(active)) {

            log.info(exchange.getLogPrefix());

            //已成功的请求直接放行
            if ("ok".equals(exchange.getRequest().getHeaders().getFirst("gatewayPass"))) {
                return chain.filter(exchange);
            }

            //静态资源放行
            if (uri.contains("swagger")
                    || uri.contains("static")
                    || uri.contains("api-doc")
                    || uri.endsWith("csrf")
                    || uri.endsWith(".js")
                    || uri.endsWith(".html")
                    || uri.endsWith(".css")
                    || uri.endsWith(".doc")) {
                exchange.getRequest().mutate().header("gatewayPass", "ok");
                return chain.filter(exchange);
            }

            //安全校验
//            if (illegalCheck(exchange)) {
//                return failResponse(exchange, HttpStatus.LENGTH_REQUIRED);
//            }
            //鉴权成功标识
            exchange.getRequest().mutate().header("gatewayPass", "ok");
            //接口登录校验
            if (loginCheck(exchange)) {
                return failResponse(exchange, HttpStatus.UNAUTHORIZED);
            }
        }

        //鉴权成功标识
        exchange.getRequest().mutate().header("gatewayPass", "ok");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 999;
    }


    /**
     * 非法校验
     */
    private Boolean illegalCheck(ServerWebExchange exchange) {
        try {
            if ("ok".equals(exchange.getRequest().getHeaders().getFirst("gatewayPass"))) {
                return false;
            }
            //获取请求头数据，加密方式:md5(md5(timestamp+ccys+rand))
            String secret = exchange.getRequest().getHeaders().getFirst("apiSecret");
            Long timestamp = Long.valueOf(exchange.getRequest().getHeaders().getFirst("timestamp"));
            String rand = exchange.getRequest().getHeaders().getFirst("rand");
            log.info("timestamp:{},rand:{},apiSecret:{}",timestamp,rand,secret);

            //验证时间戳是否在过去的5分钟内
            Long subTime = System.currentTimeMillis() - timestamp;
            if (subTime > SUB_TIMESTAMP) {
                return true;
            }

            //判断加密字符
            if (!Kit.getMD5Str(Kit.getMD5Str(timestamp + "ccys" + rand)).equals(secret)) {
                return true;
            }

            //判断加密串是否被重复使用
            if (JedisUtil.get(secret, CodeEnum.REDIS_SECURE.codeOf()) != null) {
                return true;
            }

            //加密串保留5分钟避免重复请求造成不安全
            JedisUtil.setex(secret, 60 * 5, exchange.getLogPrefix(), CodeEnum.REDIS_SECURE.codeOf());
            return false;

        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 登录校验
     */
    private Boolean loginCheck(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        Route route = (Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        log.info(route.getUri().getHost() + request.getPath().toString() + ":" + request.getMethodValue());
        if (StringUtils.isBlank(
                JedisUtil.get(route.getUri().getHost() + request.getPath().toString() + ":" + request.getMethodValue(),
                        CodeEnum.REDIS_API_PATH.codeOf()))) {
            //校验token
            String token = request.getHeaders().getFirst("token");
            //此处有坑，未引用db模块引起classNotFound异常，暂无根本解决方案，请酌情搭配偏方使用
            // (原因：db实体的BaseEntity有使用servlet,但是gateway与servlet不兼容,并且在本pom下以屏蔽servlet)
            if (StringUtils.isBlank(token) || JedisUtil.getObject(token, CodeEnum.REDIS_USER.codeOf()) == null) {
                return true;
            }
        }

        return false;
    }

    /**
     * 返回校验失败response
     */
    private Mono<Void> failResponse(ServerWebExchange exchange, HttpStatus httpStatus) {
        ServerHttpResponse response = exchange.getResponse();
        if ("1".equalsIgnoreCase(response.getHeaders().getFirst("illegalTag"))) {
            return response.setComplete();
        }
        byte[] bits = null;
        if (httpStatus.value() == 401) {
            NO_LOGIN_JSON.put("code", httpStatus.value());
            bits = NO_LOGIN_JSON.toJSONString().getBytes(StandardCharsets.UTF_8);
        } else {
            ILLEGAL_JSON.put("code", httpStatus.value());
            bits = ILLEGAL_JSON.toJSONString().getBytes(StandardCharsets.UTF_8);
        }
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(httpStatus);
        //指定编码，否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        response.getHeaders().add("illegalTag", "1");
        return response.writeWith(Mono.just(buffer));
    }
}
